package jmine.tec.persist.schema.impl.dialects;

import java.sql.Types;
import java.util.Collection;
import java.util.List;

import jmine.tec.persist.hibernate.dialect.DB2SequenceDialect;
import jmine.tec.persist.schema.api.SchemaElement;

import org.apache.commons.lang.StringUtils;

/**
 * Dialect para DB2.
 * <p/>
 * Particularidades e limitações:
 * <p/>
 * O Tablespace padrão do DB2 suporta linhas de até 8KB, portanto pode ser importante definir tablespaces alternativos para algumas tabelas
 * maiores.
 * <p/>
 * O tamanho máximo de comments é 254 caracteres, portanto é necessário truncá-los. Também não são suportados comentários com caracteres
 * especiais (acentos e etc), todos os caracteres não ASCII são removidos.
 * <p/>
 * Campos numéricos possuem precisão limitada em 31. Precisão maior gera erro. Por enquando a precisão é truncada pelo dialeto em 31, mas
 * ninguém deveria depender de uma precisão tão grande.
 * <p/>
 * 
 * @author lundberg
 */
public class DB2Dialect extends AbstractSqlDialect {

    private static final int UTF8_CHAR_SIZE = 4;

    private static final int MAX_COMMENT_LENGTH = 254;

    private static final int MAX_SMALL = 5;

    private static final int MAX_INTEGER = 10;

    private static final int MAX_BIGINT = 21;

    private static final int MAX_PRECISION = 31;

    private String defaultTablespace;

    /**
     * Construtor
     */
    public DB2Dialect() {
        super(new DB2SequenceDialect());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String renderType(int code, int length, int precision, int scale) {
        if (Types.NUMERIC == code && scale == 0) {
            if (precision < MAX_SMALL) {
                return super.renderType(Types.SMALLINT, length, precision, scale);
            } else if (precision < MAX_INTEGER) {
                return super.renderType(Types.INTEGER, length, precision, scale);
            } else if (precision < MAX_BIGINT) {
                return super.renderType(Types.BIGINT, length, precision, scale);
            } else {
                return super.renderType(code, length, Math.min(MAX_PRECISION, precision), scale);
            }
        } else if (Types.VARCHAR == code) {
            return super.renderType(code, length * UTF8_CHAR_SIZE, 0, 0);
        } else {
            return super.renderType(code, length, Math.min(MAX_PRECISION, precision), scale);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String createCommentLiteral(String comment) {
        return StringUtils.abbreviate(comment.replaceAll("[^\\p{ASCII}]", "").replaceAll("'", "''").replaceAll(";", "."),
                MAX_COMMENT_LENGTH);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<String> renderTable(String tablespace, String name, Collection<? extends SchemaElement> columns) {
        return super.renderTable(tablespace == null ? this.defaultTablespace : tablespace, name, columns);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String createColumnSuffix(boolean nullable, String defaultValue) {
        String nullability = nullable ? "" : " NOT NULL";
        String def = defaultValue == null ? "" : " DEFAULT " + defaultValue;
        return nullability + def;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String getCreateTableTablespaceSuffix(String tablespace) {
        return " IN " + tablespace;
    }

    /**
     * Define o tablespace default.
     * 
     * @param defaultTablespace defaultTablespace
     */
    public void setDefaultTablespace(String defaultTablespace) {
        this.defaultTablespace = defaultTablespace;
    }
}
